package com.asmtest.visitor;

import com.util.description.TypeDesc;

import java.util.List;
import java.util.stream.Collectors;

/**
 * @author yutianhong
 * @version 1.0
 * @since 2024/01/02 00:37
 */
public class TypeDescUtils {

    public static String getClassName(TypeDesc typeDesc) {
        if (typeDesc.isPrimitive()) {
            return typeDesc.asPrimitive().simpleName();
        }
        if (typeDesc.isArray()) {
            TypeDesc componentType = typeDesc.asArray().getComponentType();
            return getClassName(componentType) + "[]";
        }
        return typeDesc.className().replace("/", ".");
    }

    public static String getShortClassName(TypeDesc typeDesc) {
        if (typeDesc.isPrimitive()) {
            return typeDesc.asPrimitive().simpleName();
        }
        if (typeDesc.isArray()) {
            TypeDesc componentType = typeDesc.asArray().getComponentType();
            return getShortClassName(componentType) + "[]";
        }
        String className = typeDesc.className();
        int i = className.lastIndexOf('/');
        if (i == -1) {
            return className;
        }
        return className.substring(i + 1);
    }

    public static String getGenericTypeString(TypeDesc typeDesc) {
        if (typeDesc.isParameterized()) {
            List<TypeDesc> typeArguments = typeDesc.asParameterized().getTypeArguments();
            return typeArguments.stream()
                    .map(TypeDescUtils::getSimpleNameWithGeneric)
                    .collect(Collectors.joining(",", "<", ">"));
        }
        return "";
    }

    private static String getSimpleNameWithGeneric(TypeDesc type) {
        String simpleName = getShortClassName(type);
        if (type.isParameterized()) {
            simpleName += getGenericTypeString(type);
        } else if (type.isWildcard()) {
            List<TypeDesc> upperBounds = type.asWildcard().getUpperBounds();
            List<TypeDesc> lowerBounds = type.asWildcard().getLowerBounds();
            if (!upperBounds.isEmpty()) {
                TypeDesc upper = upperBounds.get(0);
                if ("java/lang/Object".equals(upper.className())) {
                    simpleName = "?";
                } else {
                    simpleName = "? extends " + getSimpleNameWithGeneric(upper);
                }
            } else if (!lowerBounds.isEmpty()) {
                TypeDesc lower = lowerBounds.get(0);
                simpleName = "? super " + getSimpleNameWithGeneric(lower);
            } else {
                simpleName = "?";
            }
        }
        return simpleName;
    }

    private TypeDescUtils() {
    }
}
